﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EvolutionManager : MonoBehaviour
{
    public int[] layerShape;
    public int populationSize;
    public int foodNum;
    public GameObject animalPrefab;
    public GameObject foodPrefab;
    public Transform startPosition;
    public Transform animalParent;
    public Material bestMat;
    public Material animalMat;
    private GA ga;
    private List<Genome> genomeList = new List<Genome>();
    private List<NeuralNetwork> neuralNetworkList = new List<NeuralNetwork>();
    private List<AnimalCtrl> animalList = new List<AnimalCtrl>();

    public GraphNeuralNetwork graphNeuralNetwork;

    public bool isSeeBestAc;
    public AnimalCtrl seeAc;
    public bool loadWeights;
    public bool startClearSave;

    public string bestKey;
    void Start()
    {
        if (startClearSave)
        {
            PlayerPrefs.DeleteKey(bestKey);
            PlayerPrefs.DeleteKey(bestKey + "Fit");
        }
        InitTest();
    }

    void Update()
    {
        RunTest();
    }

    void SaveBest(AnimalCtrl ac)
    {
        //if (PlayerPrefs.HasKey(bestKey + "Fit"))
        //{
        //    float f = PlayerPrefs.GetFloat(bestKey + "Fit");
        //    if (f > ac.fit)
        //    {
        //        return;
        //    }
        //}

        NeuralNetwork nn = ac.nn;
        double[] dw = nn.GetWeights();
        string str = "";
        for (int i = 0; i < nn.weightNum; i++)
        {
            str += dw[i] + (i == nn.weightNum - 1 ? "" : ",");
        }
        PlayerPrefs.SetFloat(bestKey + "Fit", (float)ac.fit);
        PlayerPrefs.SetString(bestKey, str);
        PlayerPrefs.Save();
    }

    void InitTest()
    {
        NeuralNetwork testNN = new NeuralNetwork(layerShape);
        graphNeuralNetwork.CreateGraphNeuralNetwork(testNN);

        ga = new GA(populationSize);
        for (int i = 0; i < populationSize; i++)
        {
            NeuralNetwork nn = new NeuralNetwork(layerShape);
            if (loadWeights)
            {
                if (PlayerPrefs.HasKey(bestKey))
                {
                    string[] strArr = PlayerPrefs.GetString(bestKey).Split(',');
                    double[] dw = new double[nn.weightNum];
                    for (int z = 0; z < nn.weightNum; z++)
                    {
                        dw[z] = double.Parse(strArr[z]);
                    }
                    nn.SetWeights(dw);
                }
                else
                {
                    Debug.LogError("没有保存最佳权重，无法加载");
                }
            }
            else
            {
                nn.RandomWeights();
            }


            Genome genome = new Genome(nn.GetWeights(), 0, nn.splitPoints);

            AnimalCtrl ac = (Instantiate(animalPrefab, startPosition.position, Quaternion.identity, animalParent) as GameObject).GetComponent<AnimalCtrl>();
            ac.nn = nn;
            ac.genome = genome;

            neuralNetworkList.Add(nn);
            genomeList.Add(genome);
            animalList.Add(ac);
        }
        for (int i = 0; i < foodNum; i++)
        {
            Instantiate(foodPrefab, new Vector3(Random.Range(-30, 30), 0, Random.Range(-30, 30)), Quaternion.identity);
        }
    }

    void RunTest()
    {
        Physics.Simulate(Time.deltaTime);
        AnimalCtrl bestAc = DrawAndGetAc();
        CheckAllDie(bestAc);
    }

    AnimalCtrl DrawAndGetAc()
    {
        AnimalCtrl bestAc = null;
        double bestFit = -9999;
        foreach (var tt in animalList)
        {
            if (tt.genome.fitness > bestFit)
            {
                bestFit = tt.genome.fitness;
                bestAc = tt;
            }
            tt.GetComponent<MeshRenderer>().material = animalMat;
        }
        bestAc.GetComponent<MeshRenderer>().material = bestMat;
        if (seeAc != null && !isSeeBestAc)
        {
            graphNeuralNetwork.SetGraphNeuralNetwork(seeAc.nn);
        }
        else if (isSeeBestAc)
        {
            graphNeuralNetwork.SetGraphNeuralNetwork(bestAc.nn);
        }
        return bestAc;
    }

    void FinishGA(AnimalCtrl bestAc)
    {
        SaveBest(bestAc);

        List<double[]> weightsList = ga.Run(genomeList);
        for (int i = 0; i < weightsList.Count; i++)
        {
            neuralNetworkList[i].SetWeights(weightsList[i]);
            animalList[i].nn = neuralNetworkList[i];
            genomeList[i] = new Genome(neuralNetworkList[i].GetWeights(), 0, neuralNetworkList[i].splitPoints);
            animalList[i].genome = genomeList[i];
            animalList[i].ReSetPosition(startPosition);
            animalList[i].health = 50;
            animalList[i].gameObject.SetActive(true);
        }
    }

    void CheckAllDie(AnimalCtrl bestAc)
    {
        bool isAllDie = true;
        foreach (var t in animalList)
        {
            if (t.gameObject.activeSelf)
            {
                isAllDie = false;
                break;
            }
        }
        if (isAllDie)
        {
            FinishGA(bestAc);
        }
    }
}
